home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 17 / develop 17 code / Window Zooming / ZoomCode.c
Encoding:
C/C++ Source or Header  |  1993-11-23  |  11.5 KB  |  304 lines  |  [TEXT/MPS ]

  1. #include <Types.h>
  2. #include <Menus.h>
  3. #include <Quickdraw.h>
  4. #include <Windows.h>
  5.  
  6. struct ZoomData {
  7.     GDHandle        screenWithLargestPartOfWindow;
  8.     unsigned long    largestArea;
  9.     Rect            windowBounds;
  10. };
  11. typedef struct ZoomData ZoomData, *ZoomDataPtr;
  12.  
  13. typedef void (*CalcIdealDocumentSizeProcPtr)(WindowPtr theWindow, Rect *idealContentSize);
  14.  
  15. enum {
  16.     kNudgeSlop    =    4,
  17.     kIconSpace    =    64
  18. };
  19.  
  20. void ZoomTheWindow(WindowPeek theWindow, short zoomState,
  21.                     CalcIdealDocumentSizeProcPtr calcRoutine);
  22. pascal void CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice,
  23.                                     long userData);
  24. short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint,
  25.                             short idealOnScreenStartPoint, short idealOnScreenEndPoint,
  26.                             short screenEdge1, short screenEdge2);
  27.  
  28. static RgnHandle GetWindowContentRegion(WindowPeek theWindow);
  29. static RgnHandle GetWindowStructureRegion(WindowPeek theWindow);
  30. static void GetWindowPortRect(WindowPeek theWindow, Rect *portRect);
  31. static void SetWindowStandardState(WindowPeek theWindow, const Rect *standardState);
  32. static void GetWindowUserState(WindowPeek theWindow, Rect *userState);
  33.  
  34.  
  35. void ZoomTheWindow(WindowPeek theWindow, short zoomState,
  36.                     CalcIdealDocumentSizeProcPtr calcRoutine)
  37. {
  38.     ZoomData    zoomData;
  39.     Rect        newStandardRect;
  40.     Rect        scratchRect;
  41.     Rect        screenRect;
  42.     Rect        portRect;
  43.     Rect        contentRegionBoundingBox;
  44.     Rect        structureRegionBoundingBox;
  45.     Rect        deviceLoopRect;
  46.     GrafPtr        currentPort;
  47.     RgnHandle    scratchRegion;
  48.     RgnHandle    contentRegion;
  49.     RgnHandle    structureRegion;
  50.     GDHandle    mainDevice;
  51.     short        horizontalAmountOffScreen;
  52.     short        verticalAmountOffScreen;
  53.     short        windowFrameTopSize;
  54.     short        windowFrameLeftSize;
  55.     short        windowFrameRightSize;
  56.     short        windowFrameBottomSize;
  57.     
  58.  
  59.     GetPort(¤tPort);
  60.     SetPort((WindowPtr) theWindow);
  61.     contentRegion = GetWindowContentRegion(theWindow);
  62.     structureRegion = GetWindowStructureRegion(theWindow);
  63.     GetWindowPortRect(theWindow, &portRect);
  64.     contentRegionBoundingBox = (**contentRegion).rgnBBox;
  65.     structureRegionBoundingBox = (**structureRegion).rgnBBox;
  66.     
  67.     // Determine the size of the window frame
  68.     windowFrameTopSize = contentRegionBoundingBox.top - 
  69.                                     structureRegionBoundingBox.top;
  70.     windowFrameLeftSize = contentRegionBoundingBox.left - 
  71.                                     structureRegionBoundingBox.left;
  72.     windowFrameRightSize = structureRegionBoundingBox.right - 
  73.                                     contentRegionBoundingBox.right;
  74.     windowFrameBottomSize = structureRegionBoundingBox.bottom - 
  75.                                     contentRegionBoundingBox.bottom;
  76.                                     
  77.     // If the window is being zoomed into the standard state, calculate the best size
  78.     // to display the window’s information.
  79.     mainDevice = GetMainDevice();
  80.     if (zoomState == inZoomOut) {
  81.         zoomData.screenWithLargestPartOfWindow = mainDevice;
  82.         zoomData.largestArea = 0;
  83.     
  84.         // Usually, we would use the content region’s bounding box to determine the monitor
  85.         // with largest portion of the window’s area. However, if the entire content region
  86.         // of the window is not on any screen, the structure region should be used instead.
  87.         scratchRegion = NewRgn();
  88.         SectRgn(GetGrayRgn(), contentRegion, scratchRegion);
  89.         if (EmptyRgn(scratchRegion))
  90.             zoomData.windowBounds = structureRegionBoundingBox;
  91.         else
  92.             zoomData.windowBounds = contentRegionBoundingBox;
  93.     
  94.         // Use DeviceLoop to walk through all the active screens to find the one with the
  95.         // largest portion of the zoomed window
  96.         deviceLoopRect = zoomData.windowBounds;
  97.         GlobalToLocal((Point *)&deviceLoopRect);
  98.         GlobalToLocal((Point *)&deviceLoopRect.bottom);
  99.         RectRgn(scratchRegion, &deviceLoopRect);
  100.         DeviceLoop(scratchRegion, &CalcWindowAreaOnScreen, (long) &zoomData,
  101.                     (DeviceLoopFlags) singleDevices);
  102.         DisposeRgn(scratchRegion);
  103.         screenRect = (**(zoomData.screenWithLargestPartOfWindow)).gdRect;
  104.         
  105.         // If the monitor being zoomed to is the main monitor, change the top of the
  106.         // useable screen area to avoid putting the title bar underneath the menubar.
  107.         if (zoomData.screenWithLargestPartOfWindow == mainDevice)
  108.             screenRect.top += GetMBarHeight();
  109.             
  110.         // Go figure out the perfect size for the window as if we had an infinitely large
  111.         // screen
  112.         (*calcRoutine)((WindowPtr) theWindow, &newStandardRect);
  113.         
  114.         // Anchor the new rectangle at the window’s current top left corner
  115.         OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
  116.         OffsetRect(&newStandardRect, contentRegionBoundingBox.left,
  117.                     contentRegionBoundingBox.top);
  118.         
  119.         // newStandardRect is the ideal size for the content area. The window frame
  120.         // needs to be accounted for when we see if the window needs to be moved,
  121.         // or resized, so add in the dimensions of the window frame.
  122.         newStandardRect.top -= windowFrameTopSize;
  123.         newStandardRect.left -= windowFrameLeftSize;
  124.         newStandardRect.right += windowFrameRightSize;
  125.         newStandardRect.bottom += windowFrameBottomSize;
  126.         
  127.         // If the new rectangle falls off the edge of the screen, nudge it so that it’s just
  128.         // on the screen. CalculateOffsetAmount determines how much of the window is offscreen.
  129.         SectRect(&newStandardRect, &screenRect, &scratchRect);
  130.         if (!EqualRect(&newStandardRect, &scratchRect)) {
  131.             horizontalAmountOffScreen = CalculateOffsetAmount(newStandardRect.left,
  132.                                                                newStandardRect.right,
  133.                                                                scratchRect.left,
  134.                                                                scratchRect.right,
  135.                                                                screenRect.left,
  136.                                                                screenRect.right);
  137.             verticalAmountOffScreen = CalculateOffsetAmount(newStandardRect.top,
  138.                                                             newStandardRect.bottom,
  139.                                                             scratchRect.top,
  140.                                                             scratchRect.bottom,
  141.                                                             screenRect.top,
  142.                                                             screenRect.bottom);
  143.             OffsetRect(&newStandardRect, horizontalAmountOffScreen,
  144.                         verticalAmountOffScreen);
  145.         }
  146.     
  147.         // If we’re still falling off the edge of the screen, that means that the perfect
  148.         // size is larger than the screen, so we need to shrink down the standard size
  149.         SectRect(&newStandardRect, &screenRect, &scratchRect);
  150.         if (!EqualRect(&newStandardRect, &scratchRect)) {
  151.  
  152.         // First shrink the width of the window. If the window is wider than the screen
  153.         // it is zooming to, we can just pin the standard rectangle to the edges of the
  154.         // screen, leaving some slop. If the window is narrower than the screen, we know
  155.         // we just nudged it into position, so nothing needs to be done.
  156.             if ((newStandardRect.right - newStandardRect.left) >
  157.                 (screenRect.right - screenRect.left)) {
  158.                 newStandardRect.left = screenRect.left + kNudgeSlop;
  159.                 newStandardRect.right = screenRect.right - kNudgeSlop;
  160.  
  161.                 if ((zoomData.screenWithLargestPartOfWindow == mainDevice) &&
  162.                     (newStandardRect.right > (screenRect.right - kIconSpace)))
  163.                     newStandardRect.right = screenRect.right - kIconSpace;
  164.             }
  165.  
  166.             // Move in the top. Like the width of the window, nothing needs to be done unless
  167.             // the window is taller than the height of the screen.
  168.             if ((newStandardRect.bottom - newStandardRect.top) >
  169.                 (screenRect.bottom - screenRect.top)) {
  170.                 newStandardRect.top = screenRect.top + kNudgeSlop;
  171.                 newStandardRect.bottom = screenRect.bottom - kNudgeSlop;
  172.             }
  173.         }
  174.  
  175.         // We’ve got the best possible window position. Remove the
  176.         // frame, slam it into the WStateData record and let ZoomWindow
  177.         // take care of the rest.
  178.         newStandardRect.top += windowFrameTopSize;
  179.         newStandardRect.left += windowFrameLeftSize;
  180.         newStandardRect.right -= windowFrameRightSize;
  181.         newStandardRect.bottom -= windowFrameBottomSize;
  182.         SetWindowStandardState(theWindow, &newStandardRect);
  183.     }
  184.     else
  185.         GetWindowUserState(theWindow, &newStandardRect);
  186.         
  187.     // If the window is still anchored at the current location, then just resize it
  188.     if ((newStandardRect.left == contentRegionBoundingBox.left) &&
  189.         (newStandardRect.top == contentRegionBoundingBox.top)) {
  190.         OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
  191.         SizeWindow((WindowPtr) theWindow, newStandardRect.right, newStandardRect.bottom,
  192.                     true);
  193.     }
  194.     else {
  195.         scratchRegion = NewRgn();
  196.         GetClip(scratchRegion);
  197.         ClipRect(&portRect);
  198.         EraseRect(&portRect);
  199.         ZoomWindow((WindowPtr) theWindow, zoomState, false);
  200.         SetClip(scratchRegion);
  201.         DisposeRgn(scratchRegion);
  202.     }
  203.     
  204.     SetPort(currentPort);
  205. }
  206.  
  207. pascal void    CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice, long userData)
  208. {
  209. #pragma unused (depth, deviceFlags)
  210.     ZoomDataPtr    zoomData = (ZoomDataPtr) userData;
  211.     long        windowAreaOnScreen;
  212.     Rect        windowPortionOnScreen;
  213.     
  214.     // Find the rectangle that encloses the intersection of the window and this screen.
  215.     SectRect(&(zoomData->windowBounds), &((**targetDevice).gdRect), &windowPortionOnScreen);
  216.     
  217.     // Offset the rectangle so that it’s right and bottom are also it’s width and height.
  218.     OffsetRect(&windowPortionOnScreen, -windowPortionOnScreen.left, -windowPortionOnScreen.top);
  219.     
  220.     // Calculate the area of the portion of the window that’s on this screen.
  221.     windowAreaOnScreen = (long) windowPortionOnScreen.right * (long) windowPortionOnScreen.bottom;
  222.     
  223.     // If this is the largest portion of the window that has been encountered so far,
  224.     // remember this screen as the potential screen to zoom to.
  225.     if (windowAreaOnScreen > zoomData->largestArea) {
  226.         zoomData->largestArea = windowAreaOnScreen;
  227.         zoomData->screenWithLargestPartOfWindow = targetDevice;
  228.     }
  229. }
  230.  
  231. // Figure out how much we need to move the window to get it entirely on the monitor.  If
  232. // the window wouldn’t fit completely on the monitor anyway, don’t move it at all; we’ll
  233. // make it fit later on.
  234.  
  235. short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint, short idealOnScreenStartPoint,
  236.                             short idealOnScreenEndPoint, short screenEdge1, short screenEdge2)
  237. {
  238.     short    offsetAmount;
  239.  
  240.     // First check to see if the window fits on the screen in this dimension.
  241.     if ((idealStartPoint < screenEdge1) && (idealEndPoint > screenEdge2))
  242.         offsetAmount = 0;
  243.     else {
  244.     
  245.         // Find out how much of the window lies off this screen by subtracting the amount of the window
  246.         // that is on the screen from the size of the entire window in this dimension. If the window
  247.         // is completely offscreen, the offset amount is going to be the distance from the ideal
  248.         // starting point to the first edge of the screen.
  249.         if ((idealOnScreenStartPoint - idealOnScreenEndPoint) == 0) {
  250.             // See if the window is lying to the left or above the screen
  251.             if (idealEndPoint < screenEdge1)
  252.                 offsetAmount = screenEdge1 - idealStartPoint + kNudgeSlop;
  253.             else
  254.             // Otherwise, it’s below or to the right of the screen
  255.                 offsetAmount = screenEdge2 - idealEndPoint - kNudgeSlop;
  256.         }
  257.         else {
  258.             // Window is already partially or completely on the screen
  259.             offsetAmount = (idealEndPoint - idealStartPoint) -
  260.                             (idealOnScreenEndPoint - idealOnScreenStartPoint);
  261.     
  262.             // If we are offscreen a little, move the window in a few more pixels from the edge of the screen.
  263.             if (offsetAmount != 0)
  264.                 offsetAmount += kNudgeSlop;
  265.             
  266.             // Check to see which side of the screen the window was falling off of, so that it can be
  267.             // nudged in the opposite direction.
  268.             if (idealEndPoint > screenEdge2)
  269.                 offsetAmount = -offsetAmount;
  270.         }
  271.     }
  272.     
  273.     return offsetAmount;
  274. }
  275.  
  276. /*
  277.     WindowRecord accessor functions
  278. */
  279.  
  280. RgnHandle GetWindowContentRegion(WindowPeek theWindow)
  281. {
  282.     return (theWindow->contRgn);
  283. }
  284.  
  285. RgnHandle GetWindowStructureRegion(WindowPeek theWindow)
  286. {
  287.     return (theWindow->strucRgn);
  288. }
  289.  
  290. void GetWindowPortRect(WindowPeek theWindow, Rect *portRect)
  291. {
  292.     *portRect = theWindow->port.portRect;
  293. }
  294.  
  295. void SetWindowStandardState(WindowPeek theWindow, const Rect *standardState)
  296. {
  297.     (**((WStateDataHandle) theWindow->dataHandle)).stdState = *standardState;
  298. }
  299.  
  300. void GetWindowUserState(WindowPeek theWindow, Rect *userState)
  301. {
  302.     *userState = (**((WStateDataHandle) theWindow->dataHandle)).userState;
  303. }
  304.